/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React from 'react'

import { getFirstParameter } from '../utils'

const ALL_THREADS = 'All Threads'
const QUERY_THREADS = 'Running Queries'

const ALL_THREAD_STATE = 'ALL'
const THREAD_STATES = [ALL_THREAD_STATE, 'RUNNABLE', 'BLOCKED', 'WAITING', 'TIMED_WAITING', 'NEW', 'TERMINATED']
const QUERY_THREAD_REGEX = new RegExp(/([0-9])*_([0-9])*_([0-9])*_.*?\.([0-9])*\.([0-9])*-([0-9])*-([0-9])*/)
const THREAD_GROUP_REGEXP = new RegExp(/(.*?)-[0-9]+/)

export class WorkerThreadList extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            serverInfo: null,
            initialized: false,
            ended: false,

            threads: null,

            snapshotTime: null,

            selectedGroup: ALL_THREADS,
            selectedThreadState: ALL_THREAD_STATE,
        }
    }

    captureSnapshot() {
        const nodeId = getFirstParameter(window.location.search)
        $.get(
            '/ui/api/worker/' + nodeId + '/thread',
            function (threads) {
                this.setState({
                    threads: WorkerThreadList.processThreads(threads),
                    snapshotTime: new Date(),
                    initialized: true,
                })
            }.bind(this)
        ).fail(
            function () {
                this.setState({
                    initialized: true,
                })
            }.bind(this)
        )
    }

    componentDidUpdate() {
        new window.ClipboardJS('.copy-button')
    }

    static processThreads(threads) {
        const result = {}

        result[ALL_THREADS] = threads

        for (let i = 0; i < threads.length; i++) {
            const thread = threads[i]
            if (thread.name.match(QUERY_THREAD_REGEX)) {
                if (!result[QUERY_THREADS]) {
                    result[QUERY_THREADS] = []
                }
                result[QUERY_THREADS].push(thread)
            }

            const match = THREAD_GROUP_REGEXP.exec(thread.name)
            const threadGroup = match ? match[1] : thread.name
            if (!result[threadGroup]) {
                result[threadGroup] = []
            }
            result[threadGroup].push(thread)
        }

        return result
    }

    handleGroupClick(selectedGroup, event) {
        this.setState({
            selectedGroup: selectedGroup,
        })
        event.preventDefault()
    }

    handleThreadStateClick(selectedThreadState, event) {
        this.setState({
            selectedThreadState: selectedThreadState,
        })
        event.preventDefault()
    }

    handleNewSnapshotClick(event) {
        this.setState({
            initialized: false,
        })
        this.captureSnapshot()
        event.preventDefault()
    }

    filterThreads(group, state) {
        return this.state.threads[group].filter((t) => t.state === state || state === ALL_THREAD_STATE)
    }

    renderGroupListItem(group) {
        return (
            <li key={group}>
                <a
                    href="#"
                    className={this.state.selectedGroup === group ? 'selected' : ''}
                    onClick={this.handleGroupClick.bind(this, group)}
                >
                    {group} ({this.filterThreads(group, this.state.selectedThreadState).length})
                </a>
            </li>
        )
    }

    renderThreadStateListItem(threadState) {
        return (
            <li key={threadState}>
                <a
                    href="#"
                    className={this.state.selectedThreadState === threadState ? 'selected' : ''}
                    onClick={this.handleThreadStateClick.bind(this, threadState)}
                >
                    {threadState} ({this.filterThreads(this.state.selectedGroup, threadState).length})
                </a>
            </li>
        )
    }

    renderStackLine(threadId) {
        return (stackLine, index) => {
            return (
                <div key={threadId + index}>
                    &nbsp;&nbsp;at {stackLine.className}.{stackLine.method}(
                    <span className="font-light">
                        {stackLine.file}:{stackLine.line}
                    </span>
                    )
                </div>
            )
        }
    }

    renderThread(threadInfo) {
        return (
            <div key={threadInfo.id}>
                <span className="font-white">
                    {threadInfo.name} {threadInfo.state} #{threadInfo.id} {threadInfo.lockOwnerId}
                </span>
                <a
                    className="copy-button"
                    data-clipboard-target={'#stack-trace-' + threadInfo.id}
                    data-toggle="tooltip"
                    data-placement="right"
                    title="Copy to clipboard"
                >
                    <span className="glyphicon glyphicon-copy" alt="Copy to clipboard" />
                </a>
                <br />
                <span className="stack-traces" id={'stack-trace-' + threadInfo.id}>
                    {threadInfo.stackTrace.map(this.renderStackLine(threadInfo.id))}
                </span>
                <div>&nbsp;</div>
            </div>
        )
    }

    render() {
        const threads = this.state.threads

        let display = null
        let toolbar = null
        if (threads === null) {
            if (this.state.initialized === false) {
                display = (
                    <div className="row error-message">
                        <div className="col-xs-12">
                            <button
                                className="btn btn-info live-button"
                                onClick={this.handleNewSnapshotClick.bind(this)}
                            >
                                Capture Snapshot
                            </button>
                        </div>
                    </div>
                )
            } else {
                display = (
                    <div className="row error-message">
                        <div className="col-xs-12">
                            <h4>Thread snapshot could not be loaded</h4>
                        </div>
                    </div>
                )
            }
        } else {
            toolbar = (
                <div className="col-xs-9">
                    <table className="header-inline-links">
                        <tbody>
                            <tr>
                                <td>
                                    <small>Snapshot at {this.state.snapshotTime.toTimeString()}</small>
                                    &nbsp;&nbsp;
                                </td>
                                <td>
                                    <button
                                        className="btn btn-info live-button"
                                        onClick={this.handleNewSnapshotClick.bind(this)}
                                    >
                                        New Snapshot
                                    </button>
                                    &nbsp;&nbsp; &nbsp;&nbsp;
                                </td>
                                <td>
                                    <div className="input-group-btn text-right">
                                        <button
                                            type="button"
                                            className="btn btn-default dropdown-toggle pull-right text-right"
                                            data-toggle="dropdown"
                                            aria-haspopup="true"
                                            aria-expanded="false"
                                        >
                                            <strong>Group:</strong> {this.state.selectedGroup}{' '}
                                            <span className="caret" />
                                        </button>
                                        <ul className="dropdown-menu">
                                            {Object.keys(threads).map((group) => this.renderGroupListItem(group))}
                                        </ul>
                                    </div>
                                </td>
                                <td>
                                    <div className="input-group-btn text-right">
                                        <button
                                            type="button"
                                            className="btn btn-default dropdown-toggle pull-right text-right"
                                            data-toggle="dropdown"
                                            aria-haspopup="true"
                                            aria-expanded="false"
                                        >
                                            <strong>State:</strong> {this.state.selectedThreadState}{' '}
                                            <span className="caret" />
                                        </button>
                                        <ul className="dropdown-menu">
                                            {THREAD_STATES.map((state) => this.renderThreadStateListItem(state))}
                                        </ul>
                                    </div>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            )

            const filteredThreads = this.filterThreads(this.state.selectedGroup, this.state.selectedThreadState)
            let displayedThreads
            if (filteredThreads.length === 0 && this.state.selectedThreadState === ALL_THREAD_STATE) {
                displayedThreads = (
                    <div className="row error-message">
                        <div className="col-xs-12">
                            <h4>No threads in group '{this.state.selectedGroup}'</h4>
                        </div>
                    </div>
                )
            } else if (filteredThreads.length === 0 && this.state.selectedGroup === ALL_THREADS) {
                displayedThreads = (
                    <div className="row error-message">
                        <div className="col-xs-12">
                            <h4>No threads with state {this.state.selectedThreadState}</h4>
                        </div>
                    </div>
                )
            } else if (filteredThreads.length === 0) {
                displayedThreads = (
                    <div className="row error-message">
                        <div className="col-xs-12">
                            <h4>
                                No threads in group '{this.state.selectedGroup}' with state{' '}
                                {this.state.selectedThreadState}
                            </h4>
                        </div>
                    </div>
                )
            } else {
                displayedThreads = <pre>{filteredThreads.map((t) => this.renderThread(t))}</pre>
            }

            display = <div id="stack-traces">{displayedThreads}</div>
        }

        return (
            <div>
                <div className="row">
                    <div className="col-xs-3">
                        <h3>
                            Thread Snapshot
                            <a
                                className="btn copy-button"
                                data-clipboard-target="#stack-traces"
                                data-toggle="tooltip"
                                data-placement="right"
                                title="Copy to clipboard"
                            >
                                <span className="glyphicon glyphicon-copy" alt="Copy to clipboard" />
                            </a>
                            &nbsp;
                        </h3>
                    </div>
                    {toolbar}
                </div>
                <div className="row">
                    <div className="col-xs-12">
                        <hr className="h3-hr" />
                        {display}
                    </div>
                </div>
            </div>
        )
    }
}
